package jfrepo;

/**
 *
 * Created : Feb 27, 2012
 *
 * @author pquiring
 */

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import javax.swing.*;

import javaforce.*;
import javaforce.linux.*;

public class MainPanel extends javax.swing.JPanel implements MouseListener, MouseMotionListener {

  /**
   * Creates new form SelectRepo
   */
  public MainPanel() {
    initComponents();
    Linux.detectDistro();
    if (Linux.distro == Linux.DistroTypes.Unknown) {
      JFAWT.showError("Error", "Unknown distro");
      System.exit(0);
    }
    initMap();
    if ((RepoApp.args != null) && (RepoApp.args.length > 1) && (RepoApp.args[0].equals("--update"))) {
      updateRepo = true;
    }
    JFTask localtask = new JFTask() {
      public boolean work() {
        task = this;
        setLabel("Loading Mirrors...");
        setProgress(taskCnt);
        initMirrors();
        setLabel("Loading Repo Lists...");
        setProgress(90);
        loadRepo();
        setLabel("Calculating Coordinates...");
        setProgress(95);
        calcMapCoords();
        loadSelections();
        updateDesc();
        repaintMap();
        setProgress(100);
        return true;
      }
    };
    ProgressDialog dialog = new ProgressDialog(null, true, localtask);
    dialog.setAutoClose(true);
    dialog.setVisible(true);
  }

  /**
   * This method is called from within the constructor to initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is always
   * regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    jLabel1 = new javax.swing.JLabel();
    protocol = new javax.swing.JComboBox();
    choose = new javax.swing.JButton();
    map = new javax.swing.JPanel();
    selection = new javax.swing.JComboBox();

    jLabel1.setText("Protocol");

    protocol.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "HTTP", "FTP", "RSYNC" }));

    choose.setText("Choose Server");
    choose.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        chooseActionPerformed(evt);
      }
    });

    map.setLayout(new java.awt.GridLayout(1, 0));

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
    this.setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(layout.createSequentialGroup()
        .addContainerGap()
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
          .addGroup(layout.createSequentialGroup()
            .addComponent(jLabel1)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(protocol, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(choose))
          .addComponent(map, javax.swing.GroupLayout.PREFERRED_SIZE, 480, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(selection, javax.swing.GroupLayout.PREFERRED_SIZE, 480, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
        .addContainerGap()
        .addComponent(map, javax.swing.GroupLayout.PREFERRED_SIZE, 240, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(jLabel1)
          .addComponent(protocol, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
          .addComponent(choose))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(selection, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    );

    map.getAccessibleContext().setAccessibleName("");
  }// </editor-fold>//GEN-END:initComponents

  private void chooseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chooseActionPerformed
    applyRepo();
  }//GEN-LAST:event_chooseActionPerformed

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton choose;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JPanel map;
  private javax.swing.JComboBox protocol;
  private javax.swing.JComboBox selection;
  // End of variables declaration//GEN-END:variables

  private class Mirror {
    double lng, lat;
    int x, y;  //translated screen coords
    String country;
    String hostname;
    String http, ftp, rsync;  //they can differ from each other
    String bandwidth;  //Kbps, Mbps or Gbps  (ya, there are a few Kbps)
    String name;
    boolean isNew;  //append to userList
    public String toString() {
      return "" + country + ":" + name + ":" + hostname + ":" + bandwidth;
    }
  }
  private JFImage imgorg, img;
  private ArrayList<Mirror> mirrors = new ArrayList<Mirror>();
  private int selIdx = -1;
  private JFTask task;
  private int taskCnt = 5;
  private boolean updateRepo = false;

  private void initMap() {
    imgorg = new JFImage();
    imgorg.load(getClass().getResourceAsStream("/worldmap.png"));
    img = new JFImage();
    img.setImageSize(imgorg.getWidth(), imgorg.getHeight());
    img.putJFImage(imgorg, 0, 0);
    img.addMouseMotionListener(this);
    img.addMouseListener(this);
    img.setResizeOperation(JFImage.ResizeOperation.CHOP);
    map.add(img);
    map.revalidate();
  }

  /** This allows connections to untrusted hosts. */
  private void initHttps() {
    TrustManager[] trustAllCerts = new TrustManager[] {
      new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
          return null;
        }
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
      }
    };
    // Let us create the factory where we can set some parameters for the connection
    try {
      SSLContext sc = SSLContext.getInstance("SSL");
      sc.init(null, trustAllCerts, new java.security.SecureRandom());
      SSLSocketFactory sslsocketfactory = (SSLSocketFactory) sc.getSocketFactory();  //this method will work with untrusted certs
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  private String getHost(String url) {
    //url = http://host/file...
    int i1 = url.indexOf("://");
    int i2 = url.indexOf("/", i1+3);
    if (i2 == -1)
      return url.substring(i1+3);
    else
      return url.substring(i1+3, i2);
  }

  private int col = -1;
  private String country = "none";
  private Mirror mirror = null;
  private void ubuntu_parseTag(XML.XMLTag tag) {
    for(int i=0;i<tag.getChildCount();i++) {
      XML.XMLTag child = tag.getChildAt(i);
      String name = child.getName();  //th, span, a
      if (name.equals("tr")) col = 0;
      if (name.equals("th")) col++;
      if (child.getChildCount() > 0) {
        ubuntu_parseTag(child);
      } else {
        if (name.equals("th")) {
          if (col == 1) {
            country = child.content.replaceAll("\n", "").trim();
            mirror = null;
          }
          continue;
        }
        if (name.equals("a")) {
          String url = child.getArg("href");
          if (url == null) continue;  //should not happen
          if (url.startsWith("/ubuntu")) {
            mirror = new Mirror();
            mirrors.add(mirror);
            mirror.isNew = true;
            mirror.http = "null";
            mirror.ftp = "null";
            mirror.rsync = "null";
            mirror.country = country;
            mirror.name = child.content.replaceAll("\n", "").trim();
            continue;
          }
          if (url.startsWith("http")) {
            mirror.http = url;
            if (mirror.hostname == null) {
              mirror.hostname = getHost(url);
            }
            continue;
          }
          if (url.startsWith("ftp")) {
            mirror.ftp = url;
            if (mirror.hostname == null) {
              mirror.hostname = getHost(url);
            }
            continue;
          }
          if (url.startsWith("rsync")) {
            mirror.rsync = url;
            if (mirror.hostname == null) {
              mirror.hostname = getHost(url);
            }
            continue;
          }
          continue;
        }
        if (name.equals("td")) {
          if (mirror == null) continue;  //country bps
          if (child.content.endsWith("bps")) {
            mirror.bandwidth = child.content.replaceAll("\n", "").trim();
          }
          continue;
        }
      }
    }
  }

  //alt fedora ??? : http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-16&arch=i386

  int tdcnt = -1;
  boolean inTable = false;
  private void fedora_parseTag(XML.XMLTag tag) {
    for(int i=0;i<tag.getChildCount();i++) {
      XML.XMLTag child = tag.getChildAt(i);
      String name = child.getName();  //th, span, a
      if (name.equals("td") && (!inTable)) {
        tdcnt++;
        if (tdcnt == 1) {
          mirror.country = child.content;
        }
        if (tdcnt == 3) {
          mirror.name = child.content;
          int cnt = 2;
          for(int a=0;a<mirrors.size()-1;a++) {
            if (mirrors.get(a).name.equals(mirror.name)) {
              //duplicate name
              mirror.name = child.content + "(" + cnt++ + ")";
              a = -1;
            }
          }
        }
        if (tdcnt == 5) {
          mirror.bandwidth = child.content + "Mbps";
        }
      }
      if (child.getChildCount() > 0) {
        if (name.equals("table")) inTable = true;
        else if (name.equals("tr")) {
          String cls = child.getArg("class");
          if ((cls != null) && ((cls.equals("even")) || (cls.equals("odd")))) {
            tdcnt = 0;
            mirror = new Mirror();
            mirrors.add(mirror);
            mirror.isNew = true;
            mirror.http = "null";
            mirror.ftp = "null";
            mirror.rsync = "null";
          }
        }
        fedora_parseTag(child);
        if (name.equals("table")) inTable = false;
      } else {
        if (name.equals("a")) {
          String href = child.getArg("href");
          if (href == null) continue;
          if (href.startsWith("http")) mirror.http = href;
          if (href.startsWith("ftp")) mirror.ftp = href;
          if (href.startsWith("rsync")) mirror.rsync = href;
          if (mirror.hostname == null) {
            mirror.hostname = getHost(href);
          }
        }
      }
    }
  }

  /** Loads mirror global location from geoiptool.com */
  private void getLocation(Mirror mirror) {
    JFLog.log("getLocation():" + mirror.name + ":" + mirror.hostname);
    InputStream is;
    BufferedReader rd;
    String line;
    StringBuffer file;
    int i1, i2;
    try {
      URL url = new URL("http://www.geoiptool.com/en/?IP=" + mirror.hostname);
      HttpURLConnection uc = (HttpURLConnection)url.openConnection();
      uc.connect();
      is = uc.getInputStream();
      rd = new BufferedReader(new InputStreamReader(is));
      file = new StringBuffer();
      while((line = rd.readLine()) != null) {
        file.append(line);
        file.append("\n");
      }
      String lns[] = file.toString().split("\n");
      boolean lat_y = false, lng_x = false;
      for(int a=0;a<lns.length;a++) {
        if (lns[a].indexOf("Latitude") != -1) {lat_y = true; continue;}
        if (lns[a].indexOf("Longitude") != -1) {lng_x = true; continue;}
        if (lat_y) {
          i1 = lns[a].indexOf(">");
          i2 = lns[a].substring(i1+1).indexOf("<");
          mirror.lat = Double.valueOf(lns[a].substring(i1+1, i1+1+i2));
          lat_y = false;
        }
        if (lng_x) {
          i1 = lns[a].indexOf(">");
          i2 = lns[a].substring(i1+1).indexOf("<");
          mirror.lng = Double.valueOf(lns[a].substring(i1+1, i1+1+i2));
          lng_x = false;
        }
      }
      task.setProgress(taskCnt++);
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  private void initMirrors() {
    switch (Linux.distro) {
      case Ubuntu: ubuntu_initMirrors(); break;
      case Fedora: fedora_initMirrors(); break;
    }
  }

  private void ubuntu_initMirrors() {
    InputStream is;
    BufferedReader rd;
    String line;
    StringBuffer file;
    int i1,i2;
    //load saved system mirrors = /etc/jfrepo-ubuntu.list
    ArrayList<Mirror> systemList = new ArrayList<Mirror>();
    try {
      FileInputStream fis = new FileInputStream("/etc/jfrepo-ubuntu.list");
      byte buf[] = JF.readAll(fis);
      fis.close();
      String lns[] = new String(buf, "UTF-8").split("\n");
      for(int i=0;i<lns.length;i++) {
        String f[] = lns[i].split("[|]");
        if (f.length != 9) {JFLog.log("warning:# fields == " + f.length); continue;}
        Mirror mirror = new Mirror();
        mirror.country = f[0];
        mirror.name = f[1];
        mirror.bandwidth = f[2];
        mirror.hostname = f[3];
        mirror.http = f[4];
        mirror.ftp = f[5];
        mirror.rsync = f[6];
        mirror.lng = Double.valueOf(f[7]);
        mirror.lat = Double.valueOf(f[8]);
        systemList.add(mirror);
      }
    } catch (Exception e) {
      JFLog.log(e);
    }
    //load saved personal mirrors = ~/.jfrepo-ubuntu.list
    ArrayList<Mirror> userList = new ArrayList<Mirror>();
    try {
      FileInputStream fis = new FileInputStream(JF.getUserPath() + "/.jfrepo-ubuntu.list");
      byte buf[] = JF.readAll(fis);
      fis.close();
      String lns[] = new String(buf, "UTF-8").split("\n");
      for(int i=0;i<lns.length;i++) {
        String f[] = lns[i].split("[|]");
        if (f.length != 9) {JFLog.log("warning:# fields == " + f.length); continue;}
        Mirror mirror = new Mirror();
        mirror.country = f[0];
        mirror.name = f[1];
        mirror.bandwidth = f[2];
        mirror.hostname = f[3];
        mirror.http = f[4];
        mirror.ftp = f[5];
        mirror.rsync = f[6];
        mirror.lng = Double.valueOf(f[7]);
        mirror.lat = Double.valueOf(f[8]);
        userList.add(mirror);
      }
    } catch (Exception e) {
      JFLog.log(e);
    }
    //load URL = https://launchpad.net/ubuntu/+archivemirrors
    JFLog.log("Loading mirrors list...");
    initHttps();
    if (updateRepo) {
      try {
        URL url = new URL("https://launchpad.net/ubuntu/+archivemirrors");
        HttpsURLConnection uc = (HttpsURLConnection)url.openConnection();
        uc.connect();
        is = uc.getInputStream();
        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        file = new StringBuffer();
        while((line = rd.readLine()) != null) {
          file.append(line);
        }
        //process file
        String mirrorsList = file.toString();
        i1 = mirrorsList.indexOf("<table");
        i2 = mirrorsList.indexOf("</table>");
        if ((i1==-1) || (i2==-1)) {JFLog.log("Error:current mirrors:unable to find mirrors table tag"); return;}
        mirrorsList = mirrorsList.substring(i1, i2 + 8);
        XML xml = new XML();
        ByteArrayInputStream buf = new ByteArrayInputStream(mirrorsList.getBytes());
        xml.read(buf);
        ubuntu_parseTag(xml.root);
      } catch (Exception e) {
        JFLog.log(e);
      }
    }
    //match those already in systemList/userList
    boolean ok = false;
    Mirror m;
    for(int i=0;i<systemList.size();i++) {
      Mirror slm = systemList.get(i);
      ok = false;
      for(int j=0;j<mirrors.size();j++) {
        m = mirrors.get(j);
        if ( (m.hostname.equals(slm.hostname)) && (m.name.equals(slm.name))) {
          m.lng = slm.lng;
          m.lat = slm.lat;
          m.isNew = false;
          ok = true;
          break;
        }
      }
      if (!ok) {
        mirrors.add(slm);
      }
    }
    for(int i=0;i<userList.size();i++) {
      Mirror ulm = userList.get(i);
      for(int j=0;j<mirrors.size();j++) {
        m = mirrors.get(j);
        if ( (m.hostname.equals(ulm.hostname)) && (m.name.equals(ulm.name))) {
          m.lng = ulm.lng;
          m.lat = ulm.lat;
          m.isNew = false;
          ok = true;
          break;
        }
      }
      if (!ok) {
        mirrors.add(ulm);
      }
    }
    //convert lat/long to x/y for those that are new
    for(int j=0;j<mirrors.size();j++) {
      m = mirrors.get(j);
      if (m.isNew) getLocation(m);
    }
    //save any new mirrors to ~/.jfrepo-ubuntu.list
    try {
      FileOutputStream fos = new FileOutputStream(JF.getUserPath() + "/.jfrepo-ubuntu.list", true);
      int cnt = 0;
      for(int j=0;j<mirrors.size();j++) {
        m = mirrors.get(j);
        if (!m.isNew) continue;
        cnt++;
        String ln = m.country + "|" + m.name + "|" + m.bandwidth + "|" + m.hostname
                 + "|" + m.http + "|" + m.ftp + "|" + m.rsync + "|" + m.lng + "|" + m.lat + "\n";
        fos.write(ln.getBytes());
      }
      fos.close();
      JFLog.log("Wrote " + cnt + " new entries to ~/.jfrepo-ubuntu.list");
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  private void fedora_initMirrors() {
    InputStream is;
    BufferedReader rd;
    String line;
    StringBuffer file;
    int i1,i2;
    //load saved system mirrors = /etc/jfrepo-fedora.list
    ArrayList<Mirror> systemList = new ArrayList<Mirror>();
    try {
      FileInputStream fis = new FileInputStream("/etc/jfrepo-fedora.list");
      byte buf[] = JF.readAll(fis);
      fis.close();
      String lns[] = new String(buf, "UTF-8").split("\n");
      for(int i=0;i<lns.length;i++) {
        String f[] = lns[i].split("[|]");
        if (f.length != 9) {JFLog.log("warning:# fields == " + f.length); continue;}
        Mirror mirror = new Mirror();
        mirror.country = f[0];
        mirror.name = f[1];
        mirror.bandwidth = f[2];
        mirror.hostname = f[3];
        mirror.http = f[4];
        mirror.ftp = f[5];
        mirror.rsync = f[6];
        mirror.lng = Double.valueOf(f[7]);
        mirror.lat = Double.valueOf(f[8]);
        systemList.add(mirror);
      }
    } catch (Exception e) {
      JFLog.log(e);
    }
    //load saved personal mirrors = ~/.jfrepo.list
    ArrayList<Mirror> userList = new ArrayList<Mirror>();
    try {
      FileInputStream fis = new FileInputStream(JF.getUserPath() + "/.jfrepo-fedora.list");
      byte buf[] = JF.readAll(fis);
      fis.close();
      String lns[] = new String(buf, "UTF-8").split("\n");
      for(int i=0;i<lns.length;i++) {
        String f[] = lns[i].split("[|]");
        if (f.length != 9) {JFLog.log("warning:# fields == " + f.length); continue;}
        Mirror mirror = new Mirror();
        mirror.country = f[0];
        mirror.name = f[1];
        mirror.bandwidth = f[2];
        mirror.hostname = f[3];
        mirror.http = f[4];
        mirror.ftp = f[5];
        mirror.rsync = f[6];
        mirror.lng = Double.valueOf(f[7]);
        mirror.lat = Double.valueOf(f[8]);
        userList.add(mirror);
      }
    } catch (Exception e) {
      JFLog.log(e);
    }
    //load URL = http://mirrors.fedoraproject.org/publiclist/Fedora/
    JFLog.log("Loading mirrors list...");
    initHttps();
    if (updateRepo) {
      try {
        URL url = new URL("http://mirrors.fedoraproject.org/publiclist/Fedora/");
        HttpURLConnection uc = (HttpURLConnection)url.openConnection();
        uc.connect();
        is = uc.getInputStream();
        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        file = new StringBuffer();
        while((line = rd.readLine()) != null) {
          file.append(line);
        }
        //process file
        String mirrorsList = file.toString();
        i1 = mirrorsList.indexOf("<table");
        if (i1==-1) {JFLog.log("Error:current mirrors:unable to find mirrors table tag"); return;}
        i1 = mirrorsList.indexOf("<table", i1+1);  //2nd <table ...> tag
        i2 = mirrorsList.lastIndexOf("</table>");
        if ((i1==-1) || (i2==-1)) {JFLog.log("Error:current mirrors:unable to find mirrors table tag"); return;}
        mirrorsList = mirrorsList.substring(i1, i2 + 8);
        XML xml = new XML();
        ByteArrayInputStream buf = new ByteArrayInputStream(mirrorsList.getBytes());
        xml.read(buf);
        fedora_parseTag(xml.root);
      } catch (Exception e) {
        JFLog.log(e);
      }
    }
    //match those already in systemList/userList
    boolean ok = false;
    Mirror m;
    for(int i=0;i<systemList.size();i++) {
      Mirror slm = systemList.get(i);
      ok = false;
      for(int j=0;j<mirrors.size();j++) {
        m = mirrors.get(j);
        if ( (m.hostname.equals(slm.hostname)) && (m.name.equals(slm.name))) {
          m.lng = slm.lng;
          m.lat = slm.lat;
          m.isNew = false;
          ok = true;
          break;
        }
      }
      if (!ok) {
        mirrors.add(slm);
      }
    }
    for(int i=0;i<userList.size();i++) {
      Mirror ulm = userList.get(i);
      for(int j=0;j<mirrors.size();j++) {
        m = mirrors.get(j);
        if ( (m.hostname.equals(ulm.hostname)) && (m.name.equals(ulm.name))) {
          m.lng = ulm.lng;
          m.lat = ulm.lat;
          m.isNew = false;
          ok = true;
          break;
        }
      }
      if (!ok) {
        mirrors.add(ulm);
      }
    }
    //get lat/long for those that are new
    for(int j=0;j<mirrors.size();j++) {
      m = mirrors.get(j);
      if (m.isNew) getLocation(m);
    }
    //save any new mirrors to ~/.jfrepo.list
    try {
      FileOutputStream fos = new FileOutputStream(JF.getUserPath() + "/.jfrepo-fedora.list", true);
      int cnt = 0;
      for(int j=0;j<mirrors.size();j++) {
        m = mirrors.get(j);
        if (!m.isNew) continue;
        cnt++;
        String ln = m.country + "|" + m.name + "|" + m.bandwidth + "|" + m.hostname
                 + "|" + m.http + "|" + m.ftp + "|" + m.rsync + "|" + m.lng + "|" + m.lat + "\n";
        fos.write(ln.getBytes());
      }
      fos.close();
      JFLog.log("Wrote " + cnt + " new entries to ~/.jfrepo-fedora.list");
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  //see http://en.wikipedia.org/wiki/Miller_cylindrical_projection
  //image and equations taken from Ubuntu ubiquity
  private int lngToX(double lng) {
    double width = img.getWidth();
    double x = (lng * (Math.PI / 180.0)) + Math.PI;
    x = x / (2.0 * Math.PI);
    x = x * width;
    return (int)x;
  }

  private int latToY(double lat) {
    double height = img.getHeight();
    double y = 1.25 * Math.log(Math.tan(
      (0.25 * Math.PI) + (0.4 * (lat * (Math.PI / 180.0)))));
    //100 % = 4.6068250867599998
    y /= 2.30341254338;
    y *= 1.3;  //scale adjust
    y = y * -height/2.0 + height/2.0;
    return (int)y;
  }

  private void calcMapCoords() {
    for(int a=0;a<mirrors.size();a++) {
      Mirror m = mirrors.get(a);
      m.x = lngToX(m.lng);
      m.y = latToY(m.lat);
    }
  }

  private void repaintMap() {
    img.putJFImage(imgorg, 0, 0);
    Graphics2D g = img.getGraphics2D();
    for(int a=0;a<mirrors.size();a++) {
      Mirror m = mirrors.get(a);
      if (a == selIdx)
        g.setColor(new Color(0x00ff00));  //green dot (selected)
      else
        g.setColor(new Color(0xff0000));  //red dot (not selected)
      int rad = 4;  //TODO : change size based on bandwidth
      g.fillOval(m.x-rad/2, m.y-rad/2, rad, rad);
    }
    img.repaint();
  }

  private void updateDesc() {
    if (selIdx == -1) return;
    selection.setSelectedIndex(selIdx);
  }

  public void loadRepo() {
    switch (Linux.distro) {
      case Ubuntu: _loadRepo("/etc/apt/sources.list"); break;
      case Fedora: _loadRepo("/etc/yum.repo.d/fedora.repo"); break;
    }
  }

  public void _loadRepo(String file) {
    try {
      FileInputStream fis = new FileInputStream(file);
      String apt = new String(JF.readAll(fis));
      fis.close();
      String lns[] = apt.split("\n");
      if (lns[0].startsWith("#$jfrepo$")) {
        int idx;
        String f[] = lns[0].split(":");
        String hostname = null, name = null;
        for(int a=0;a<f.length;a++) {
          if (f[a].startsWith("hostname=")) {
            idx = f[a].indexOf("=");
            hostname = f[a].substring(idx + 1);
          }
          if (f[a].startsWith("name=")) {
            idx = f[a].indexOf("=");
            name = f[a].substring(idx + 1);
          }
        }
        if ((hostname == null)||(name == null)) return;
        for(int b=0;b<mirrors.size();b++) {
          Mirror m = mirrors.get(b);
          if ((m.hostname.equals(hostname)) && (m.name.equals(name))) {
            selIdx = b;
            break;
          }
        }
      }
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  private void applyRepo() {
    switch (Linux.distro) {
      case Ubuntu: ubuntu_applyRepo(); break;
      case Fedora: fedora_applyRepo(); break;
    }
  }

  private void ubuntu_applyRepo() {
    if (selIdx == -1) {
      JFAWT.showError("Error", "Please select a site first");
      return;
    }
    try {
      FileInputStream fis = new FileInputStream("/etc/lsb-release");
      String lsb = new String(JF.readAll(fis));
      fis.close();
      String lns[] = lsb.split("\n");
      String codename = null;
      for(int a=0;a<lns.length;a++) {
        if (lns[a].startsWith("DISTRIB_CODENAME")) {
          int idx = lns[a].indexOf("=");
          codename = lns[a].substring(idx+1);
          break;
        }
      }
      if (codename == null) throw new Exception("/etc/lsb-release corrupt : unable to read DISTRIB_CODENAME");
      InputStream is = getClass().getResourceAsStream("/sources.list");
      String list = new String(JF.readAll(is));
      is.close();
      list = list.replaceAll("[$]DISTRIB_CODENAME[$]", codename);
      String selURL = null;
      Mirror m = mirrors.get(selIdx);
      switch (protocol.getSelectedIndex()) {
        case 0: selURL = m.http; break;
        case 1: selURL = m.ftp; break;
        case 2: selURL = m.rsync; break;
      }
      list = list.replaceAll("[$]HOSTNAME[$]", m.hostname);
      list = list.replaceAll("[$]NAME[$]", m.name);
      if ((selURL == null) || (selURL.equals("null"))) throw new Exception("internal error");  //shouldn't happen
      list = list.replaceAll("[$]URL[$]", selURL);
      File tmpFile = File.createTempFile("ubuntu", ".repo");
      FileOutputStream fos = new FileOutputStream(tmpFile);
      fos.write(list.getBytes());
      fos.close();
      if (!Linux.copyFile(tmpFile.getAbsolutePath(), "/etc/apt/sources.list")) {
        JFAWT.showError("Error", "Failed to apply repo");
        return;
      }
      tmpFile.delete();
      System.exit(0);
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  private void fedora_applyRepo() {
    if (selIdx == -1) {
      JFAWT.showError("Error", "Please select a site first");
      return;
    }
    try {
      InputStream is = getClass().getResourceAsStream("/fedora.repo");
      String list = new String(JF.readAll(is));
      is.close();
      String selURL = null;
      Mirror m = mirrors.get(selIdx);
      switch (protocol.getSelectedIndex()) {
        case 0: selURL = m.http; break;
        case 1: selURL = m.ftp; break;
        case 2: selURL = m.rsync; break;
      }
      if ((selURL == null) || (selURL.equals("null"))) throw new Exception("internal error");  //shouldn't happen
      list = list.replaceAll("[$]URL[$]", selURL);
      list = list.replaceAll("[$]HOSTNAME[$]", m.hostname);
      list = list.replaceAll("[$]NAME[$]", m.name);
      File tmpFile1 = File.createTempFile("fedora", ".repo");
      FileOutputStream fos = new FileOutputStream(tmpFile1);
      fos.write(list.getBytes());
      fos.close();

      is = getClass().getResourceAsStream("/fedora-updates.repo");
      list = new String(JF.readAll(is));
      is.close();
      list = list.replaceAll("[$]URL[$]", selURL);
      File tmpFile2 = File.createTempFile("fedora-updates", ".repo");
      fos = new FileOutputStream(tmpFile2);
      fos.write(list.getBytes());
      fos.close();

      is = getClass().getResourceAsStream("/fedora-updates-testing.repo");
      list = new String(JF.readAll(is));
      is.close();
      list = list.replaceAll("[$]URL[$]", selURL);
      File tmpFile3 = File.createTempFile("fedora-updates-testing", ".repo");
      fos = new FileOutputStream(tmpFile3);
      fos.write(list.getBytes());
      fos.close();

      boolean ok = true;
      if (!Linux.runScript(new String[] {
        "cp " + tmpFile1.getAbsolutePath() + " /etc/yum.repo.d/fedora.repo",
        "cp " + tmpFile2.getAbsolutePath() + " /etc/yum.repo.d/fedora-updates.repo",
        "cp " + tmpFile3.getAbsolutePath() + " /etc/yum.repo.d/fedora-updates-testing.repo"
      })) {
        JFAWT.showError("Error", "Failed to apply repo");
        ok = false;
      }
      tmpFile1.delete();
      tmpFile2.delete();
      tmpFile3.delete();
      if (!ok) return;
      System.exit(0);
    } catch (Exception e) {
      JFLog.log(e);
    }
  }

  private void loadSelections() {
    int cnt = mirrors.size();
    for(int a=0;a<cnt;a++) {
      Mirror m = mirrors.get(a);
      selection.addItem(m);
    }
  }

  public void mouseClicked(MouseEvent e) {
    //find closest to point
    int x = e.getX();
    int y = e.getY();
    int bd = 0xffffff;
    int bi = -1;
    Mirror m;
    for(int a=0;a<mirrors.size();a++) {
      m = mirrors.get(a);
      int d = (int)Math.sqrt(Math.pow(m.x - x, 2) + Math.pow(m.y - y, 2));
      if (d < bd) {
        bi = a;
        bd = d;
      }
    }
    if (bi == -1) return;
    selIdx = bi;
    m = mirrors.get(selIdx);
    updateDesc();
    repaintMap();
    protocol.removeAllItems();
    if (!m.http.equals("null")) protocol.addItem("HTTP");
    if (!m.ftp.equals("null")) protocol.addItem("FTP");
    if (!m.rsync.equals("null")) protocol.addItem("RSYNC");
  }
  public void mouseEntered(MouseEvent e) { }
  public void mouseExited(MouseEvent e) { }
  public void mousePressed(MouseEvent e) { }
  public void mouseReleased(MouseEvent e) { }

  public void mouseDragged(MouseEvent e) { }
  public void mouseMoved(MouseEvent e) { }
}
